package com.zz.tijian.service.impl;


import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zz.tijian.async.AsyncReportService;
import com.zz.tijian.config.AliPayConfig;
import com.zz.tijian.constants.RedisConstants;
import com.zz.tijian.entity.*;
import com.zz.tijian.entity.req.*;
import com.zz.tijian.entity.res.*;
import com.zz.tijian.expections.BadRequestException;
import com.zz.tijian.expections.CommonException;
import com.zz.tijian.handle.mq.PayConsumer;
import com.zz.tijian.handle.mq.PayProducer;
import com.zz.tijian.handle.mq.dto.PayMQDto;
import com.zz.tijian.handle.pay.res.AlipayStrategy;
import com.zz.tijian.handle.pay.res.PayStrategy;
import com.zz.tijian.handle.redis.RedisLockUtil;
import com.zz.tijian.handle.mq.RefundProducer;
import com.zz.tijian.mapper.OrdersMapper;
import com.zz.tijian.service.*;
import com.zz.tijian.utils.UserContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.alipay.api.response.AlipayTradeRefundResponse;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.*;

/**
* @author 17958
* @description 针对表【orders】的数据库操作Service实现
* @createDate 2024-06-13 12:05:47
*/
@Service
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders>
    implements OrdersService {

    @Autowired
    private RedisLockUtil redisLockUtil;
    @Autowired
    private RefundProducer refundProducer;
    @Autowired
    private HospitalService hospitalService;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private SetmealService setmealService;
    @Autowired
    private CalendarService calendarService;
    @Autowired
    private UsersService usersService;
    @Autowired
    private AsyncReportService asyncReportService;

    @Autowired
    private AlipayClient alipayClient;
    @Autowired
    private AliPayConfig aliPayConfig;
    @Override
    @Transactional
    public Result<OrderDetailVo> placeOrder(PlaceOrderDto placeOrderDto) {
        // 获取当前用户信息
        String userId = UserContext.currentUserId();
        String lockKey = RedisConstants.ORDER_LOCK_PREFIX + userId;
        String requestId = RedisLockUtil.generateRequestId(userId, RedisConstants.ORDERID);

        try {
            // 尝试获取锁，过期时间设定为5秒，防止锁死
            if (!redisLockUtil.tryLock(lockKey, requestId, 5)) {
                throw new CommonException("系统繁忙，请稍后重试");
            }
            // 检查是否已经存在相同的未支付订单
            Orders preOne = checkExistingOrder(userId, placeOrderDto);
            if (preOne != null) {
                OrderDetailVo orderDetailVo = getOrderDetail(preOne.getOrderId());
                return Result.ok(orderDetailVo);
            }
            // 检查预约余量
            Integer count = checkOneDay(placeOrderDto);
            if (count <= 0) {
                throw new CommonException("预约余量不足");
            }

            // 创建新订单
            Orders orders = createNewOrder(userId, placeOrderDto);
//            // 插入套餐报告
//            insertPackageReports(orders);

            // 返回订单详情
            OrderDetailVo orderDetailVo = getOrderDetail(orders.getOrderId());
            return Result.ok(orderDetailVo);

        } finally {
            // 最终释放锁
            redisLockUtil.releaseLock(lockKey, requestId);
        }
    }

    // 检查是否已经存在相同的未支付订单
    private Orders checkExistingOrder(String userId, PlaceOrderDto placeOrderDto) {
        LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Orders::getUserId, userId)
                .eq(Orders::getHpId, placeOrderDto.getHpId())
                .eq(Orders::getSmId, placeOrderDto.getSmId())
                .eq(Orders::getOrderDate, placeOrderDto.getOrderDate())
                .eq(Orders::getState, 1);
        return this.getOne(queryWrapper);
    }

    // 创建新订单并保存到数据库
    private Orders createNewOrder(String userId, PlaceOrderDto placeOrderDto) {
        Orders orders = new Orders();
        orders.setUserId(userId);
        orders.setSmId(placeOrderDto.getSmId());
        orders.setHpId(placeOrderDto.getHpId());
        orders.setOrderDate(placeOrderDto.getOrderDate());
        orders.setState(1); // 订单状态为1表示已下单，未支付
        this.save(orders);
        return orders;
    }


    /*
    订单支付
     */

    @Override
    @Transactional
    public Result<Object> payOrder(PayOrderDto payOrderDto,PayStrategy payStrategy) {
        Integer orderId = payOrderDto.getOrderId();
        //TODO 支付接口幂等性
        Orders selectOne = this.getById(orderId);
        if(selectOne==null){
            throw new BadRequestException("订单不存在!");
        }
        if(selectOne.getState()==2){
            throw new CommonException("订单已支付,请勿重复支付!");
        }

        OrderPlusVo orderPlus = this.getOrderPlus(orderId);
        PayDto payDto = new PayDto();
        payDto.setOrderId(orderId);
        payDto.setPrice(orderPlus.getPrice().toString());
        payDto.setSetmealName(orderPlus.getSetmealName());

        /*//TODO 支付处理
        //1.0调用第三方支付
        OrderPlusVo orderPlus = this.getOrderPlus(orderId);
        // 实例化客户端,填入所需参数
        AlipayTradePagePayRequest request = new
                AlipayTradePagePayRequest();
        // 在公共参数中设置回跳和通知地址
        request.setReturnUrl(aliPayConfig.getRETURN_URL());
        //request.setNotifyUrl(aliPayConfig.getNOTIFY_URL());

        // 商户订单号，商户网站订单系统中唯一订单号，必填
        String out_trade_no = orderId.toString();
        // 付款金额，必填
        // String total_amount = order.getSetmeal().getPrice().toString();

        String total_amount = orderPlus.getPrice().toString();
        // 订单名称，必填
        String subject = "熙心健康体检";
        // 商品描述，可空
        String body = orderPlus.getSetmealName().toString();
        request.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
                + "\"total_amount\":\"" + total_amount + "\","
                + "\"subject\":\"" + subject + "\","
                + "\"body\":\"" + body + "\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        String form = "";
        try {
            form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
        } catch (AlipayApiException e) {
            log.error("支付宝调用异常", e);
        }*/
        Result<Object> result = payStrategy.pay(payDto);
        return result;
    }

    @Override
    public void paySuccess(Integer orderId) {
        Orders selectOne = this.getById(orderId);
        // 2.0支付成功修改订单状态
        selectOne.setState(2);
        ordersMapper.updateById(selectOne);
        log.info("订单状态已更新为已支付, orderId: {}", orderId);

        // 3.0 给线程池提交异步任务
        asyncReportService.executeAsync(selectOne);
        log.info("异步任务已提交, orderId: {}", orderId);
    }

    @Override
    @Transactional
    public Result<Boolean> cancelOrder(CancelOrderDto cancelOrderDto) {
        //取消订单或退款 需要根据状态进行转换
        Orders order = this.getById(cancelOrderDto.getOrderId());
        if(order.getState()==1) {
            // 1.0状态1 订单创建未支付 可以直接设为状态6取消
            order.setState(6);
            ordersMapper.updateById(order);
        }else if(order.getState()==2) {
            //2.0 状态 2，已经支付，需要进行退款业务，设置为状态5退款中
            order.setState(5);
            ordersMapper.updateById(order);
            //2.1发送消息到退款业务消息队列
            refundProducer.sendRefundMessage(order.getOrderId());
        }else {
            throw new CommonException("参数有误！");
        }
        //取消为6
        return Result.ok(true);
    }

    /*
     用户退款后续操作,mq监听执行
     */
    @Override
    public void processRefund(Integer orderId) {
        // 幂等性检查
        Orders order = ordersMapper.selectById(orderId);
        if (order == null || order.getState() == 6) { // 6: 已取消
            return; // 已处理或订单不存在
        }
        // 实际退款操作，如调用第三方支付平台的退款API
        boolean success = performRefund(orderId);
        log.info("mq退款业务");
        if (success) {
            // 更新订单状态为已退款
            order.setState(7); // 7: 已退款
            ordersMapper.updateById(order);
        } else {
            throw new RuntimeException("退款失败！");
        }
    }
    /*
    支付后的消息处理
    */
    @Override
    public void processPay(PayMQDto payMQDto) {
        //TODO 支付后续处理，消息幂等判断
        Orders order = this.getById(payMQDto.getOrderId());

        // 插入套餐报告
        /*insertPackageReports(order);*/
        log.info("pay消费中！！！1");
    }

    @Override
    public Integer getOrderPrice(Integer orderId) {
        Orders selectOne = this.getById(orderId);
        if(selectOne==null){
            throw new BadRequestException("订单不存在!");
        }
        Integer smId = selectOne.getSmId();
        Setmeal setmeal = setmealService.getById(smId);
        return setmeal.getPrice();
    }

    @Override
    public OrderPlusVo getOrderPlus(Integer orderId) {
        Orders select = ordersMapper.selectById(orderId);
        if(select==null){
            throw new BadRequestException("订单不存在!");
        }
        OrderPlusVo orderPlusVo=new OrderPlusVo();
        orderPlusVo.setOrderId(select.getOrderId());
        orderPlusVo.setOrderDate(select.getOrderDate());
        orderPlusVo.setUserId(select.getUserId());
        orderPlusVo.setHpId(select.getHpId());
        orderPlusVo.setSmId(select.getSmId());
        orderPlusVo.setState(select.getState());
        Setmeal setmeal = setmealService.getById(select.getSmId());
        orderPlusVo.setSetmealName(setmeal.getName());
        orderPlusVo.setPrice(setmeal.getPrice());

        return orderPlusVo;
    }


    private boolean performRefund(Integer orderId) {
        //TODO 模拟实际退款操作
        AlipayTradeRefundRequest req = new AlipayTradeRefundRequest();
        // 订单支付时传入的商户订单号,不能和 trade_no同时为空
        String trade_no = orderId.toString();
        // 需要退款的金额
        Integer orderPrice = this.getOrderPrice(orderId);
        String refund_amount = orderPrice.toString();
        //标识一次退款请求，同一笔交易多次退款需要保证唯一。如需部分退款，则此参数必传；不传该参数则代表全额退款
        String out_request_no = new String(UUID.randomUUID().toString());
        // 字符转义很重要
        req.setBizContent("{\"out_trade_no\":\""+ trade_no +"\","
                + "\"refund_amount\":\""+ refund_amount +"\","
                + "\"out_request_no\":\""+ out_request_no +"\"}");

        AlipayTradeRefundResponse resp = null;
        try {
            //int i = 1 / 0; // 模拟异常
            resp = alipayClient.execute(req);
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return false;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        if (resp.isSuccess()) {
            return true;
        } else {
            return false;
        }

    }
    private boolean performPay(Integer orderId) {
        //TODO 模拟实际支付操作
        log.info("支付成功");
        return true;
    }
    /*
    查询某套餐一天的预约余量,用于下单的防超卖判断
     */
    private Integer checkOneDay(PlaceOrderDto placeOrderDto){
        //TODO 防超卖判断
        return 1;
    }

    @Override
    public Result<AvailabilityVo> checkAvailability(CheckAvailabilityDto dto) {
        //医院每周周预约量安排
        Hospital hospital = hospitalService.getById(dto.getHpId());
        String rule = hospital.getRule();
        Map<Integer,Integer> weekAvailability=parseRuleToMap(rule);
        //  日期-余量，初始化每天预约量,范围是当天开始算的一个月内
        LocalDate nowDate=LocalDate.now();

        Map<LocalDate,Integer> availability=initializeAvailabilityMap(nowDate,weekAvailability);
        //计算当月每天的余量，从订单中获取已经预约的量
        //1.先获取30天后日期
//        dto.setEndDate(dto.getStartDate().withDayOfMonth(dto.getStartDate().lengthOfMonth()));
        dto.setEndDate(dto.getStartDate().plusDays(30));
        //2.获取当月后面每天的预约量,只需要查询当前往后的一个月内的记录
        CheckAvailabilityDto dto2=new CheckAvailabilityDto();
        BeanUtils.copyProperties(dto,dto2);
        dto2.setStartDate(nowDate);
        List<OrderCountByDateVO> orderCounts = ordersMapper.selectOrderCountByDateRange(dto2);
        //3. 更新余量
        for(OrderCountByDateVO ocVo:orderCounts){
            Integer pre = availability.getOrDefault(ocVo.getOrderDate(),0);
            availability.put(ocVo.getOrderDate(),pre-ocVo.getOrderCount());
        }
        //4. 构造万年历
        int year = dto.getStartDate().getYear();
        int month = dto.getStartDate().getMonthValue();
        AvailabilityVo availabilityVo = calendarService.getAvailability(year, month);
        //5. 给万年历里面赋值余量
        for(AvailabilityVo.DayAvailability temp: availabilityVo.getDays()){
            LocalDate date = temp.getDate();
            Integer count = availability.get(date);
            if(count==null||count<=0){
                temp.setOpen(false);
            }else {
                temp.setOpen(true);
                temp.setRemainingSlots(count);
            }
        }
        return Result.ok(availabilityVo);

    }

    @Override
    public Result<List<OrderOfSetmealVO>> selectOrderByUserId() {
        String userId = UserContext.currentUserId();
        List<OrderOfSetmealVO> orderOfSetmealVOS = ordersMapper.selectOrderWithSetmeal(userId);
        Collections.sort(orderOfSetmealVOS, (o1, o2) -> o2.getOrderDate().compareTo(o1.getOrderDate()));

        return  Result.ok(orderOfSetmealVOS);
    }

    @Override
    public Result<OrderDetailVo> selectByOrderId(Integer orderId) {
        OrderDetailVo orderDetailVo = this.getOrderDetail(orderId);
        return Result.ok(orderDetailVo);
    }



    public OrderDetailVo getOrderDetail(Integer orderId){
        OrderDetailVo orderDetailVo =new OrderDetailVo();
        Orders order = this.getById(orderId);
        BeanUtils.copyProperties(order, orderDetailVo);
        SetmealVo setmealVo = setmealService.getSetmealWithCheckItems(order.getSmId());
        orderDetailVo.setSetmealVo(setmealVo);
        Hospital hospital = hospitalService.getById(order.getHpId());
        orderDetailVo.setHospital(hospital);

        Users userInfo = usersService.getById(UserContext.currentUserId());
        orderDetailVo.setUsers(userInfo);
        return orderDetailVo;
    }

    private Map<LocalDate, Integer> initializeAvailabilityMap(LocalDate dateDto
            , Map<Integer, Integer> weekAvailability) {
        Map<LocalDate, Integer> availability = new HashMap<>();
        LocalDate startDate = dateDto;
//        LocalDate endDate = startDate.plusMonths(1).withDayOfMonth(1).minusDays(1); // 获取当前月的最后一天
        LocalDate endDate = startDate.plusDays(30);

        // 初始化从当前日期开始，直到本月月底的所有日期
        while (!startDate.isAfter(endDate)) {
            DayOfWeek dayOfWeek = startDate.getDayOfWeek();
            int weekValue = dayOfWeek.getValue();
            weekValue = (weekValue == 7) ? 0 : weekValue; // 如果是星期日，转化为0

            Integer count = weekAvailability.get(weekValue);
            availability.put(startDate, count);
            startDate = startDate.plusDays(1); // 下一天
        }
        return availability;

    }
    /*
     获取每个医院的每周每天预约量的安排
     */
    public static Map<Integer, Integer> parseRuleToMap(String rule) {
        String[] parts = rule.split(",");
        Map<Integer, Integer> weekAvailability = new HashMap<>();
        for (int i = 0; i < parts.length; i++) {
            weekAvailability.put(i, Integer.parseInt(parts[i]));
        }
        return weekAvailability;
    }

}




